using System;
using Headspring.Training.Domain.Model;
using Headspring.Training.Domain.Model.StateCommands;
using Headspring.Training.Domain.Services;
using Headspring.Training.Domain.Services.Impl;
using Headspring.Training.WebUI.Services.Impl;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

namespace Headspring.Training.WebUI.UnitTests.Services
{
	[TestFixture]
	public class WorkflowCoordinatorTester
	{
		[Test]
		public void ShouldSaveWorkOrder()
		{
			MockWorkOrderRepository repository = new MockWorkOrderRepository();
			IWorkflowCoordinator coordinator = new WorkflowCoordinator(repository, null, null, null, null);
			WorkOrder order = new WorkOrder();
			coordinator.SaveWorkOrder(order);

			Assert.That(repository.WorkOrderSaved, Is.EqualTo(order));
		}

		public class MockWorkOrderRepository : IWorkOrderRepository
		{
			public WorkOrder WorkOrderSaved;

			public void Save(WorkOrder workOrder)
			{
				WorkOrderSaved = workOrder;
			}

			public WorkOrder GetWorkOrder(string number)
			{
				throw new NotImplementedException();
			}

			public WorkOrder[] GetWorkOrders(WorkOrderSearchSpecification specification)
			{
				throw new NotImplementedException();
			}
		}

		[Test]
		public void ShouldSendMessage()
		{
			MockRepository mocks = new MockRepository();
			IUserSession session = mocks.CreateMock<IUserSession>();
			session.PushUserMessage(null);
			FlashMessage actualMessage = null;
			LastCall.IgnoreArguments().Do(new Action<FlashMessage>(
			                              	delegate(FlashMessage obj) { actualMessage = obj; }));
			mocks.ReplayAll();

			IWorkflowCoordinator coordinator = new WorkflowCoordinator(null, session, null, null, null);
			coordinator.SendMessage("howdy pardner");

			mocks.VerifyAll();
			Assert.That(actualMessage.Type, Is.EqualTo(FlashMessage.MessageType.Message));
			Assert.That(actualMessage.Message, Is.EqualTo("howdy pardner"));
		}

		[Test]
		public void ShouldSendError()
		{
			MockRepository mocks = new MockRepository();
			IUserSession session = mocks.CreateMock<IUserSession>();
			session.PushUserMessage(null);
			FlashMessage actualMessage = null;
			LastCall.IgnoreArguments().Do(new Action<FlashMessage>(delegate(FlashMessage obj) { actualMessage = obj; }));
			mocks.ReplayAll();

			IWorkflowCoordinator coordinator = new WorkflowCoordinator(null, session, null, null, null);
			coordinator.SendError("whoa");

			mocks.VerifyAll();
			Assert.That(actualMessage.Type, Is.EqualTo(FlashMessage.MessageType.Error));
			Assert.That(actualMessage.Message, Is.EqualTo("whoa"));
		}


		[Test]
		public void ShouldSendNotification()
		{
			WorkOrder workOrder = new WorkOrder();
			IStateCommand command = new StateCommandStub(WorkOrderStatus.InProgress, workOrder);

			MockRepository mocks = new MockRepository();

			IUserSession session = mocks.CreateMock<IUserSession>();
			IWorkOrderRepository repository = mocks.CreateMock<IWorkOrderRepository>();
			INotificationFactory factory = mocks.CreateMock<INotificationFactory>();
			INotificationSender sender = mocks.CreateMock<INotificationSender>();

			WorkOrderNotification notification = new WorkOrderNotification();
			notification.Recipient = new EmployeeStub(NotificationType.Both);
			Expect.Call(factory.CreateNotification(command, workOrder)).Return(notification);

			sender.Send(notification);
			mocks.ReplayAll();

			IWorkflowCoordinator coordinator = new WorkflowCoordinator(repository, session,
			                                                           factory, sender, new StubbedClock(DateTime.Now));
			coordinator.SendNotification(command, workOrder);

			mocks.VerifyAll();
		}

		[Test]
		public void ShouldNotSendNotification()
		{
			WorkOrder workOrder = new WorkOrder();
			IStateCommand command = new StateCommandStub(WorkOrderStatus.InProgress, workOrder);

			MockRepository mocks = new MockRepository();

			IUserSession session = mocks.CreateMock<IUserSession>();
			IWorkOrderRepository repository = mocks.CreateMock<IWorkOrderRepository>();
			INotificationFactory factory = mocks.CreateMock<INotificationFactory>();
			INotificationSender sender = mocks.CreateMock<INotificationSender>();

			WorkOrderNotification notification = new WorkOrderNotification();
			notification.Recipient = new EmployeeStub(NotificationType.None);
			Expect.Call(factory.CreateNotification(command, workOrder)).Return(notification);

			mocks.ReplayAll();

			IWorkflowCoordinator coordinator = new WorkflowCoordinator(repository, session,
			                                                           factory, sender, new StubbedClock(DateTime.Now));
			coordinator.SendNotification(command, workOrder);

			mocks.VerifyAll();
		}

		[Test]
		public void ShouldSendNotificationOnWeekend()
		{
			WorkOrder workOrder = new WorkOrder();
			IStateCommand command = new StateCommandStub(WorkOrderStatus.InProgress, workOrder);

			MockRepository mocks = new MockRepository();

			IUserSession session = mocks.CreateMock<IUserSession>();
			IWorkOrderRepository repository = mocks.CreateMock<IWorkOrderRepository>();
			INotificationFactory factory = mocks.CreateMock<INotificationFactory>();
			INotificationSender sender = mocks.CreateMock<INotificationSender>();
			Employee employee = mocks.DynamicMock<Employee>();
			Expect.Call(employee.GetNotificationTypeFor(DayOfWeek.Saturday)).Return(NotificationType.Both);
			IClock clock = new StubbedClock(new DateTime(2008, 2, 2));

			WorkOrderNotification notification = new WorkOrderNotification();
			notification.Recipient = employee;
			Expect.Call(factory.CreateNotification(command, workOrder)).Return(notification);
			sender.Send(notification);
			mocks.ReplayAll();

			IWorkflowCoordinator coordinator = new WorkflowCoordinator(repository, session,
			                                                           factory, sender, clock);
			coordinator.SendNotification(command, workOrder);

			mocks.VerifyAll();
		}

		public class EmployeeStub : Employee
		{
			private readonly NotificationType _wantsNotification;

			public EmployeeStub(NotificationType wantsNotification)
			{
				_wantsNotification = wantsNotification;
			}

			public override NotificationType GetNotificationTypeFor(DayOfWeek dayOfWeek)
			{
				return _wantsNotification;
			}
		}

		public class StateCommandStub : IStateCommand
		{
			private readonly WorkOrderStatus _status;
			private readonly WorkOrder _workOrder;

			public StateCommandStub(WorkOrderStatus status, WorkOrder workOrder)
			{
				_status = status;
				_workOrder = workOrder;
			}

			public bool IsValid()
			{
				return true;
			}

			public void Execute(IWorkflowCoordinator coordinator)
			{
			}

			public string TransitionVerbPresentTense
			{
				get { throw new NotImplementedException(); }
			}

			public bool Matches(string commandName)
			{
				throw new NotImplementedException();
			}

			public WorkOrderStatus GetBeginStatus()
			{
				return _status;
			}
		}
	}
}